home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Libris Britannia 4
/
science library(b).zip
/
science library(b)
/
UTILITIE
/
CONVERSI
/
H400.ZIP
/
ZSIM22.ZIP
/
CPMBIOS.ASM
< prev
next >
Wrap
Assembly Source File
|
1993-06-16
|
97KB
|
4,204 lines
;
; BIOS FUER DEN CP/M EMULATOR
;
;
; (C) 1990,1991,1992,1993 by Jürgen Weber
;
; Version: 1.0
; 1.1 Load/Save disparameters with /F fname
; 1.11 bios read signals Dos File End
; 1.2 conin translates Csr keys to WS
; time functon and blink attribute
; zsim12 uploaded to simtel
; 1.21 Option /Snnn to restrain ramdisc size
; 1.22 look first for `COMSPEC` then \COMMAND.COM
; free 128K mem for OS-Shell
; some bug fixes
; do not save cpm_drv to disc parameter file anymore
; 1.3 can be assembled with option to make an interrupt
; request with every timer tick
; 1.4 user bios call for file transfer to/from msdos
; menu option reset screen
; 1.5 select drive 0 after warm boot from ctrl-brk-menu
; menu option swap drives
; no longer swap to root dir at os shell
; 1.6 IOBYTE support:
; function 6 and 7 can be set to the serial port
; 1.7 option /D to set physical drive
; option /H to get option help
; 2.0 Z80 emulation is now 20% faster
; zsim20 uploaded to simtel
; 2.1 harddisc file C:
; work with only one disk drive no longer supported
; 2.2 option /A to start with A: as ramdisc
; option /N to start with autologin disabled
; combined functions for low level read and write
; 2.21 took NOP as default opcode to execute at int 0
; as zsid enables ints at the go command
; stored path of swapfile at os-shell, as swapfile
; couldn't be reloaded if user changed directory
.286
DEFAULT_IOBYTE EQU 10000001b ; lpt0,,,video
CVERSION equ '2.21' ; to display on screen
CVERSNR equ 22h ; to return if requested
CVERSDATE equ '14/06/93'
; CHANGE THE FELLOWING TO THE PHYSICAL DISK
; YOU WANT TO USE FOR CPM
IFNDEF PHYS_DRV
PHYS_DRV EQU 0 ; phys number of pc drive to use as CP/M floppy
ENDIF
IF PHYS_DRV GT 1
%OUT ERROR: PHYSICAL CP/M DRIVE SET TO ILLEGAL VALUE: VALID IS 0 OR 1
ERR
ENDIF
HARD_DISC_TOO EQU -1
HARD_DISC_SIZE EQU 8192 ; the maximal amount for CP/M 2.2 is 8 M
; IF TIMER_INTS THEN
; at every PC Timertick a z80 interrupt request is made
; (if default int mode 0 is set execute DATABUS_BTE,
; elseif int mode 1 do a RST 38H
; else (int mode 2) call word ptr [I_Reg shl 8 + (DATABUS_BTE shl 1) )
; (Z80 emulator defaults to interrupt DISabled, IM 0)
TIMER_INTS EQU 0
DATABUS_BTE EQU 0H ; NOP, as secure default
INCLUDE Z80EMU.INC
EXTRN crtout:near,crtin:near,crtinstat:near,reset_crt:near,sel_scr_page:near
EXTRN cursor_off:near,cursor_on:near
PUBLIC prg_exit,bios88
PUBLIC breakflag
%nosyms ; keine symbols im listing
warn ; alle warnings an
DOSSEG ; UNBEDINGT notwendig, da fuer Speicherverwaltung des Z80 Segments
; und der Ramdisk Stacksegment als letztes kommen muss
; auch mussen Stack- und Codesegment Klassen 'STACK' bzw.
; 'CODE'bekommen
LOCALS ; fuer locale Labels in Prozeduren
JUMPS ; automatische Sprunglaengenanpassung
CALLZ macro label ; Call if zero
local exit
jnz short exit
call label
exit:
endm
CALLNZ macro label ; Call if not zero
local exit
jz short exit
call label
exit:
endm
CALLC macro label ; Call if carry
local exit
jnc short exit
call label
exit:
endm
MOVZ macro targ,src ; move if zero
local exit
jnz short exit
mov targ,src
exit:
endm
MOVNZ macro targ,src ; move if not zero
local exit
jz short exit
mov targ,src
exit:
endm
TSTNCLR MACRO FLAG ; Z-> war nicht gesetzt
CMP FLAG,FALSE
MOV FLAG,FALSE
ENDM
;
; segmente des emulators:
;
; emulator_seg : emulator und bios code
; emudata_seg : emulator data und z80 bios code
; stack_seg : emulator und bios stack
; z80cpu_seg : Segment des emulierten z80, 64k, dummy at 0 (in z80seg_adr)
; ramdisc_seg : ramdisc fuer cp/m
;
include dos.inc ; dos functionen
;
; ******* KONSTANTEN ********
;
DEFAULT_DMA EQU 80H
RECORD_LEN EQU 80H
ZBIOSLEN equ (zbiosend-zbiosbeg)
ZBIOSDISPL equ (0ffffh-ZBIOSLEN) and 0ff00h
; dies bewirkt Start auf neuer Seite, damit Bios auf XX00 anfaengt
ZBIOS equ offset zbiosbeg+ZBIOSDISPL
CTRL_BREAK_INT EQU 1BH
ESC_KEY EQU 27
EMU_SCR_PAGE EQU 0
DOS_SCR_PAGE EQU 1
; folgende Konstanten werden als Flags zur Addressberechnung
; im Sektorbuffer benoetigt
READ_OP EQU 0
WRITE_OP EQU 0FFH
BDOSLEN equ 1600h ; Laenge ccp+bdos cp/m 2.2
BDOS_CHK_SUM equ 2d88h ; Checksumme ueber Bdos Copyright Meldung
SYS_FRST_SEC EQU 28
; erster absoluter Sektor, den Bdos auf SysSpuren belegt
SYS_SECS EQU BDOSLEN/RECORD_LEN ; Anzahl Sektoren, die System belegt
TRACK_BUF_LEN EQU 10*512
CPM_EOF EQU 26
RMD equ 1 ; phys Number of ramdisk IN CP/M
IF HARD_DISC_TOO
HARD_DISC equ 2 ; phys Number of harddisk IN CP/M
ENDIF
PHYS_SEKLEN equ 512
NSECTS equ 2
IF HARD_DISC_TOO
NDISKS equ 3 ; last disk # +1
ELSE
NDISKS equ 2 ; last disk # +1
ENDIF
BPS equ 2
; -------------------------------------
;
; emulator data segment
;
; --------------------------------------
emudata_seg segment para public 'DATA'
include zbios.inc
dpb_cpc dpb <SPTC,BSHC,BLMC,EXMC,DSMC,DRMC,AL0C,AL1C,CKSC,OFFC>
dpb_dta dpb <SPTD,BSHD,BLMD,EXMD,DSMD,DRMD,AL0D,AL1D,CKSD,OFFD>
dpb_eig dpb <SPTD,BSHD,BLMD,EXMD,DSMD,DRMD,AL0D,AL1D,CKSD,OFFD>
dpb_cpm86SS dpb <SPT0,BSH0,BLM0,EXM0,DSM0,DRM0,AL00,AL10,CKS0,OFF0>
dpb_cpm86DS dpb <SPT1,BSH1,BLM1,EXM1,DSM1,DRM1,AL01,AL11,CKS1,OFF1>
ramdisklen dw (?)
lasttrack_rd db (?) ; 0..127 (512K)
PUNCH_BUF_SIZE equ 64
READER_BUF_SIZE equ 64
ld_rd_string db 27,'E',27,'H','Initializing ramdisc ...',13,10,10,0
ld_hd_string db 'Initializing CP/M harddisc ...',0
start_string db 27,'E',27,'Y',32+6,32+24,27,'p'
db 'ZSIM'
db 27,'Y',32+8,32+18
db 'THE Z80 / CP/M Emulator',27,'q'
db 27,'Y',32+10,32+18
db 'Free for personal use'
db 27,'Y',32+12,32+18
db 27,'p','USE AT YOUR OWN RISC !',27,'q'
db 27,'Y',32+15,32+18
db '(C) 1990,1993 by '
db 27,'Y',32+17,32+18
db 'Jürgen G. Weber'
db 27,'Y',32+18,32+18
db 'Wiesentalstraße 1'
db 27,'Y',32+19,32+18
db 'D-74523 Schwäbisch Hall'
db 27,'Y',32+20,32+18
db 'Federal Republic of Germany'
db 27,'Y',32+24,32+17,27,'p'
db 'Insert a CP/M disk into drive '
drv_letter db 'A'
db ': then press any key'
db 27,'q'
db 0
help_str db 13,10
db 'ZSIM version '
db CVERSION
db ' - Z80 EMULATOR + CP/M 80 BIOS'
db 13,10,10
db '(C) 1990,1992,1993 by Jürgen Weber',13,10,10
db 'Options:',13,10,10
db '/Dn n=0,1 Use PC disc drive n',13,10
db '/F <parmfile> Use disc parameter file <parmfile>',13,10
db '/H or /? Get this help',13,10
db '/Snnn Restrain ramdisc size to nnn K',13,10
db '/C Create the harddisc file HARDDISC.CPM',13,10
db '/N Start with autologin disabled',13,10
db '/A Start with ramdisc=A:',13,10
db 0
help_str_end equ $
db 'ZSIM is copyrighted by Jürgen Weber but'
db ' it looks for the fellowing string: '
bdos_str db ' COPYRIGHT (C) 1979, DIGITAL RESEARCH '
bdos_str_end equ this byte
cpmsys_fileName db 'CPMSYS.CPM',0
ramdisc_fname db 'RAMDISC.CPM',0
harddisc_fname db 'HARDDISC.CPM',0
punch_fileName db 'PUNCH.CPM',0
reader_fileName db 'READER.CPM',0
temp_fname db 'EMUOVL.$$$',0
fmt_file_flg db FALSE
new_hdsk_flg db FALSE
fmt_file_name db 80 dup (?)
file_promt_str db 'Enter Disk Parameter filename: ',0
file_promt_str_end equ $
menutab label word
dw m_esc
dw m_continue
dw m_save_rd_quit
dw m_quit
dw m_save_rd
dw m_del_pf
dw m_init_rdr
dw m_shell
dw m_edit_disk_parm
dw m_save_d_parm
dw m_load_d_parm
dw m_login_disc
dw m_swap_drives
dw m_reset_scr
dw m_w_boot
dw m_info
MESSAGE_COUNT=($-menutab)/2 - 1 ; ESC has no entry string
; remember to set on entry in menutab for every menu_str
menu_strs db '\Continue','|'
db 'Save \Ramdisc/Quit','|'
db '\Quit','|'
db '\Save Ramdisc','|'
db '\Delete Punch File','|'
db '\Init Reader','|'
db '\OS Shell','|'
db 'Disk \Parameters','|'
db 'Sa\ve Parameters','|'
db '\Load Parameters','|'
db 'Logi\n Disc','|'
db 'Swap Driv\es','|'
db 'Rese\t Screen','|'
db '\Warm Boot','|'
db '\About','|'
db 0
menu_strs_end equ this byte
MESG_ESC equ 0
LONGEST_MESSAGE=20
prg_ext_box_res dw 0ffffh
disk_err_txt db 'Disk Error '
disk_err_num db 2 dup (?)
db 'H, Class '
disk_err_cls db 2 dup (?)
db 'H. Press ESC|',0
disk_err_txt_end equ this byte
f_not_found_txt db 'File not found. Press ESC|',0
f_not_found_txt_end equ this byte
no_mem_txt db 'Not enough memory to shell. Press ESC|',0
no_mem_txt_end equ this byte
on_sign_string equ this byte
db 27,'E',27,'H'
db 'jgw 64K cp/m 80 bios ver '
db CVERSION
db ' -- '
db CVERSDATE
db ' (C) 1990,1993 by Jürgen G. Weber'
db 13,10,10
db 'BDOS: ',27,'j',0
bad_format db 13,10
db 'Unknown disc format. Insert new disc into drive '
drv_letter1 db 'A'
db ': and press any key'
db 13,10,0
no_sys db 13,10
db 'Could not read System sectors. Insert'
db ' new disc and press any key'
cr_lf_txt db 13,10,0
control_c_txt db '^C...',0
reset_scr_txt db 27,'@',0
mfulstr db 13,10,'Not enough memory',13,10
mfsend equ this byte
illparm_str db 13,10,'Illegal Command Line Parameter',13,10
illparm_strend equ this byte
illdriv_str db 13,10,'CP/M drive my not be current MS-Dos drive.',13,10
illdriv_strend equ this byte
dma_txt db 13,10
db 'Fatal: DMA Boundary Crossing'
db 13,10
dma_txt_end equ this byte
exit_str db 'Type EXIT to return to CP/M Emulator ...',13,10
exit_str_end equ this byte
exec_par_block equ this word
dw 0
dw offset exec_cmd_line
dw seg exec_cmd_line
dd 0
dd 0
exec_cmd_line equ this word
db ec_str_end-ec_str
ec_str db ''
ec_str_end equ this byte
db 13
exec_usr_flg db FALSE
exec_usr_lne equ $
db 0
db '/C '
usr_lne equ $
db 80 dup (?)
no_cc_str db 13,10,'COMMAND.COM not found. Press ESC.',13,10
no_cc_str_end equ this byte
exec_fname db '\COMMAND.COM',0
comspec db 80 dup (?)
var_comspec db 'COMSPEC='
var_comspec_len equ $-var_comspec
good_dpb_edit db (?)
db 2 dup (?)
breakflag db FALSE
old_int1b dd (?)
stackpoi dw (?)
sp_save dw (?)
ss_save dw (?)
z80seg_adr dw (?)
rdseg_adr dw (?)
rdlen dw (?)
max_rd_size dw 1024 ; default auf maximal
exec_mem_start dw (?)
default_dta dd (?)
psp_adr dw (?)
prog_len dw (?)
mem_end dw (?)
ovl_base dw (?)
hd_drive_flag db (?)
ms_current_drive db (?)
tmp_word dw (?)
z80_pc dw ?
ccp_adr dw ?
bdos_adr dw ?
bios_adr dw ?
month_tab db 31,28,31,30,31,30,31,31,30,31,30,31
; flag, dass im Speicher noch ein nicht auf disk geschriebener
; nicht-dir Track ist
write_flag db FALSE
in_boot_flag db FALSE
home_flag db FALSE
last_track_written db 0,0
dirtrack db 0,0
r_track db 0,0
w_track db 0,0
track db 0,0
sector db 0,0
last_track_read db 0ffh ; default unmoeglich, noch nicht gelesen
ms_phys_sec_len db (?)
pspt_last db 9
phys_tracks_last db 40
frstps_last db 041h
dirtr_last db 2
dpb_last dw offset dpb_cpc
cpm86dd_flag db FALSE
drv_swap_flag db FALSE
; ACHTUNG: die Reihenfolge der folgenden Vars nicht
; verändern, da sie so ans Modula UP übergeben werden
cpmdrv_set_flg db FALSE
cpm_drive db PHYS_DRV
first_phys_sec db 41h
phys_tracks db (?)
phys_sec_pt db (?)
cpm_phys_sec_len db 2
autologin_flag db TRUE
; end Reihenfolge wichtig
retry_count db 5
side db 0
dmaad dw ? ;direct memory address
diskno db ? ;disk number 0-15
punch_buf_entries dw (?)
punch_buf_ptr dw (?)
reader_empty_flag db TRUE
reader_buf_entries dw (?)
reader_buf_ptr dw (?)
reader_file_pointer dw 0,0
new_dosfilespecflg db (?)
openin_handle dw 0
openout_handle dw (?)
hd_handle dw 0 ; default: none
dosfilespec db 80 dup (?)
temppath db 'X:\'
db 80 dup (?)
; zeigt auf Bufferadresse des Tracks, der geschrieben werden soll
outbuf_ptr dw (?)
cpm_bdos_buf db BDOSLEN dup (?)
dirtrbuf db TRACK_BUF_LEN dup (?)
secbuf db TRACK_BUF_LEN dup (?)
wrtbuf db TRACK_BUF_LEN dup (?)
punch_buf db PUNCH_BUF_SIZE dup (?)
reader_buf db READER_BUF_SIZE dup (?)
; dass die Buffer in der EXE-Datei erscheinen, laesst sich
; durch die Verwendung einer Gruppe mit den Buffern als
; Extra Segment umgehen
; jedoch muss dann bei jedem offset der Gruppenname angegeben werden
emudata_seg ends
; ------------------------
DATA segment para public 'DATA'
; Dummysegment zur Kombinierung mit Modula 2
DATA ends
STACK_SIZE EQU 1000H
stack_seg segment para stack 'STACK'
dw STACK_SIZE dup (?) ; Stack ist recht gross
; wegen Modula 2 UPs
stack_seg ends
z80cpu_seg segment at 0 ; dummy
z80cpu_seg ends
; -------------------------------------
; emulator und bios segment start
; -------------------------------------
invalidate_rdbuf macro
mov last_track_read,0ffh
endm
emulator_seg segment para public 'CODE'
assume cs:emulator_seg,ds:emudata_seg,es:z80cpu_seg,ss:stack_seg
m2_ds dw (?) ; fmodula routinen erwarten
; bei cs:0 ihr DS
init proc ; emulator start
mov ax,emudata_seg
mov ds,ax ; damit Vars angesprochen werden koennen
mov stackpoi,sp
mov bx,ss ; finde Programmende
mov ax,sp
mov cl,4
shr ax,cl ; ax:=4
add bx,ax ; bx=>programm ende
inc bx ; vorsichtshalber
push bx
mov ax,es ; es:0 => PSP
mov psp_adr,ax
mov bx,ax
mov si,0
mov ax,[es:si+2] ; ax=> mem end
mov mem_end,ax
push ax
sub ax,2000h
mov ovl_base,ax
pop ax
push ax
sub ax,bx ; mem end - prg start
mov prog_len,ax
pop ax ; mem end
pop bx ; prg end
sub ax,bx ; ax:=free mem
mov z80seg_adr,bx
mov cx,1000h ; = (10000h / 16) = 64K in Paragraphs
add bx,cx
mov rdseg_adr,bx
sub ax,cx
jc @@memful
mov ramdisklen,ax
PUSHR <es,bx>
DOS GET_DTA
mov word ptr default_dta,bx
mov bx,es
mov word ptr default_dta+2,bx
POPR <bx,es>
call parse_cmd_line
mov dx,offset illparm_str
mov cx,illparm_strend-illparm_str
jc short abort ; Fehler: falscher Parameter
DOS GET_DISK_DRIVE
mov ms_current_drive,al
cmp al,cpm_drive
mov dx,offset illdriv_str
mov cx,illdriv_strend-illdriv_str
je short abort ; error: dosdrive equals cp/m drive
mov bx,offset ld_rd_string
call puts
call init_ramdisc
mov bx,offset ld_hd_string
call puts
call init_harddisc
mov al,cpm_drive
add al,'A'
mov drv_letter,al
mov drv_letter1,al
mov bx,offset start_string
call puts
call crtin ; wait for keypressed
call patch_int
mov ax,z80seg_adr
call clearz80 ; loesche z80 mem
call patch_box
; physikalische Sektorlaenge merken
push es
mov ax,0
mov es,ax
les di,[es:78h]
mov al,es:[di+3]
pop es
mov ms_phys_sec_len,al
;
; offset z80 bios start merken
;
mov bios_adr,ZBIOSDISPL ; bios Sprungleiste einrichten
call z80ini ; reset z80 cpu
mov ax,z80seg_adr
mov es,ax
mov ds,ax
;
; weiter mit cp/m cold boot
;
mov al,0
jmp bios88
@@memful:
mov cx,mfsend-mfulstr
mov dx,offset mfulstr
abort:
mov bx,STDERR
DOS WRITE_TO_HANDLE
mov al,1
DOS TERMINATE_EXE
init endp
get_comspec proc
; on entry: ds->psp
; si=seg data !
PUSHR <ds,es,si,di>
mov ax,[ds:2ch]
mov ds,ax
mov es,si
mov bx,0
@@loop:
mov si,bx
inc bx
mov di,offset var_comspec
mov cx,var_comspec_len
repz cmpsb
jcxz short @@found
cmp word ptr [ds:si-1],0
jnz @@loop
@@exit:
POPR <di,si,es,ds>
ret
@@found:
mov di,offset comspec
call strcpy
jmp @@exit
get_comspec endp
get_startup_path proc
get_startup_path endp
parse_cmd_line proc
LOCAL dsseg:WORD = AUTO_SIZE
push bp
mov bp,sp
sub sp,AUTO_SIZE
PUSHR <ax,bx,cx,di,si,ds,es>
mov fmt_file_flg,FALSE
mov ax,ds
mov dsseg,ds
mov si,ax
mov es,ax ; es:=prog vars
mov ax,psp_adr
mov ds,ax
call get_comspec
mov di,offset fmt_file_name
mov si,80h ; si => Kommandozeilenparam
lodsb ; count
mov bh,0
mov bl,al
mov [si+bx],bh ; 0
or al,al
mov al,FALSE
jz @@parm_done
@@findslash:
; don't forget string help_str
lodsb
or al,al
jz @@parm_done
cmp al," "
jz short @@findslash ; +DEC CX
cmp al,"/"
jz short @@good_sep
cmp al,"-"
jnz @@parm_err
@@good_sep:
lodsb
cmp al,'?'
jz helpnabort
and al,not ('a'-'A') ; toupper
cmp al,'H'
jz helpnabort
cmp al,'C'
jnz short @@no_create
mov bx,offset new_hdsk_flg
mov byte ptr [es:bx],TRUE
jmp @@findslash
@@no_create:
cmp al,'N'
jnz short @@no_no_autolog
mov bx,offset autologin_flag
mov byte ptr [es:bx],FALSE
jmp @@findslash
@@no_no_autolog:
cmp al,'A'
jnz short @@no_swap
mov bx,offset drv_swap_flag
mov byte ptr [es:bx],TRUE
jmp @@findslash
@@no_swap:
cmp al,'F'
jz short @@parm_file
cmp al,'D'
jz short @@phys_drive
cmp al,'S'
jnz @@parm_err
call @@parse_num
cmp ax,640
ja @@parm_err
cmp ax,64
jb short @@parm_err
mov bx,offset max_rd_size
mov [es:bx],ax
jmp @@findslash
@@parm_file:
mov di,offset fmt_file_name
@@SKIP_BLN:
lodsb
cmp al," "
jz @@SKIP_BLN; +DEC CX
stosb ; 1. non-blank
@@cp_loop:
lodsb
stosb
or al,al
jz short @@name_done
cmp al,' '
jz short @@name_done
cmp al,13
jz short @@name_done
jmp @@cp_loop
@@name_done:
dec si
mov al,0
stosb ; Filename Endzeichen
mov di,offset fmt_file_flg
mov byte ptr es:[di],TRUE
jmp @@findslash
@@phys_drive:
lodsb
cmp al," "
jz @@phys_drive ; +DEC CX
sub al,'0'
jc short @@parm_err
cmp al,1
ja short @@parm_err
push ds
mov ds,dsseg
mov cpm_drive,al
mov cpmdrv_set_flg,TRUE
pop ds
jmp @@findslash
@@parse_num:
mov bx,10
mov ax,0
mov dh,0
@@addloop:
mov dl,[si]
or dl,dl
jz short @@done
inc si
sub dl,'0'
jc short @@done
cmp dl,9
ja short @@done
push dx
mul bx
pop dx
add ax,dx
jmp short @@addloop
@@done:
ret
@@parm_done:
POPR <es,ds,si,di,cx,bx,ax>
clc
add sp,AUTO_SIZE
pop bp
ret
@@parm_err:
POPR <es,ds,si,di,cx,bx,ax>
stc
add sp,AUTO_SIZE
pop bp
ret
parse_cmd_line endp
helpnabort proc
mov ax,emudata_seg
mov ds,ax
mov cx,help_str_end-help_str
mov dx,offset help_str
jmp abort
helpnabort endp
tst_hd_drive proc
PUSHR <ax,dx>
mov hd_drive_flag,FALSE
mov dl,cpm_drive
mov ah,15h ; Laufwerktyp ?
int 13h
jc short @@exit ; PC,XT
cmp ah,2 ; erkennt DiskWechsel
jnz short @@exit
mov hd_drive_flag,TRUE
@@exit:
POPR <dx,ax>
ret
tst_hd_drive endp
;
; der Interupt 1bH (Control-Break Interupt) muß auf eine Routine
; gelegt werden, die ein Abbruch Flag setzt
; Dieses wird dann bei jedem Bios Aufruf ausgewertet
;
patch_int proc
PUSHR <ds,es,ax,bx,dx>
mov al,CTRL_BREAK_INT
DOS GET_VECTOR
mov word ptr old_int1b,bx
mov bx,es
mov word ptr old_int1b+2,bx
mov dx,offset new_int1b
push cs
pop ds
mov al,CTRL_BREAK_INT
DOS SET_VECTOR
if TIMER_INTS
mov al,1cH ; timer
DOS GET_VECTOR
mov word ptr cs:old_int1C,bx
mov bx,es
mov word ptr cs:old_int1C+2,bx
mov dx,offset new_int1C
push cs
pop ds
mov al,1cH
DOS SET_VECTOR
endif
POPR <dx,bx,ax,es,ds>
ret
patch_int endp
restore_int proc
push ds
lds dx,old_int1b
mov al,CTRL_BREAK_INT
DOS SET_VECTOR
if TIMER_INTS
lds dx,cs:old_int1C
mov al,1Ch
DOS SET_VECTOR
endif
pop ds
ret
restore_int endp
new_int1b proc
push ax
push ds
mov ax,emudata_seg
mov ds,ax
cmp byte ptr cs:in_emu_flg,TRUE
jnz short @@not_in_emu
call ctrl_break_req
jmp short @@exit
@@not_in_emu:
mov breakflag,TRUE ; nur wenn gerade bios code abgearbeitet wird
@@exit:
pop ds
pop ax
iret
new_int1b endp
if TIMER_INTS
old_int1C dd (?)
; at every timer tick do an interrupt request
new_int1C proc
cmp byte ptr cs:in_emu_flg,TRUE
push ax
mov al,DATABUS_BTE
CALLZ interrupt_request ; only if Z80 is running
pop ax
jmp dword ptr cs:[old_int1C]
new_int1C endp
endif ; TIMER_INTS
; wird auch von Z80EMU.OP76 HALT aufgerufen
prg_exit proc FAR
PUSHR <ds,es,ax>
PUSHR <bx,cx,dx,si>
mov ax,emudata_seg
mov ds,ax
mov ax,z80seg_adr
mov es,ax
call restore_int
mov breakflag,FALSE
call wrt_out_punch
mov cx,menu_strs_end-menu_strs ; high(messages)
mov si,offset menu_strs ; offs(messages)
call box_call
sal ax,1
mov di,ax
call menutab[di]
call patch_int
POPR <si,dx,cx,bx>
POPR <ax,es,ds>
ret
prg_exit endp
; Menü durch ESC abgebrochen
m_esc proc
ret
m_esc endp
; Menüpunkt Emulation fortfahren
m_continue proc
ret
m_continue endp
; Menüpunkt Quit
m_quit proc
; etwaige Zeichen im Punch Buffer noch ausschreiben
call wrt_out_punch
mov cl,31 ; GotoXY(1,25)
call crtout
mov cl,1
call crtout
mov cl,25
call crtout
mov al,0
ABORT_EMU:
DOS TERMINATE_EXE
m_quit endp
; Menüpunkt Ramdisk speichern
m_save_rd proc
call save_ramdisc
ret
m_save_rd endp
; Menüpunkt Ramdisk speichern + Quit
m_save_rd_quit proc
call save_ramdisc
jmp short m_quit
m_save_rd_quit endp
; Menüpunkt Punch buffer file löschen
m_del_pf proc
call del_punch_file
ret
m_del_pf endp
; Menüpunkt Reader initialisieren
m_init_rdr proc
call reset_reader_buf
ret
m_init_rdr endp
; Menüpunkt OS Shell
m_shell proc
call dos_shell
ret
m_shell endp
update_disk_pars proc
PUSHR <AX,BX>
mov cpm86dd_flag,FALSE
mov ah,0
mov al,dpb0.off-dpb0.spt ; off
call get_dpb_entrie ; nach bx
mov dirtrack,bl
POPR <BX,AX>
ret
update_disk_pars endp
; Menüpunkt Diskettenparameter editieren
m_edit_disk_parm proc
call edit_disk_pars
cmp good_dpb_edit,1
jnz short @@exit
call update_disk_pars
cmp in_boot_flag,TRUE
jz short @@exit ; mit boot weitermachen
call m_w_boot
@@exit:
ret
m_edit_disk_parm endp
prepare_fname proc
mov di,offset fmt_file_name
push di
mov cx,80
mov si,offset file_promt_str
mov bx,file_promt_str_end-file_promt_str
call input_string
pop bx
or byte ptr [bx],0 ; Test ob was eingegeben, nein => Z
ret
prepare_fname endp
; Menüpunkt Diskettenparameter speichern
m_save_d_parm proc
call prepare_fname
jz short @@exit
call save_disk_pars
@@exit:
ret
m_save_d_parm endp
; Menüpunkt Diskettenparameter laden
m_load_d_parm proc
call prepare_fname
jz short @@exit
call load_disk_pars
jmp short m_w_boot
@@exit:
ret
m_load_d_parm endp
; Menüpunkt CP/M warm boot durchführen
m_w_boot proc
mov sp,stackpoi
call patch_int
mov ax,z80seg_adr
mov es,ax
mov di,CDISK
and byte ptr es:[di],11b ; select sensible disk
call init_iobyte
mov al,1
jmp bios88
m_w_boot endp
m_login_disc proc
push word ptr autologin_flag
mov autologin_flag,TRUE
call login_disc
invalidate_rdbuf
pop word ptr autologin_flag
ret
m_login_disc endp
; Menüpunkt About Author
m_info proc
call about_author
ret
m_info endp
m_swap_drives proc
cmp drv_swap_flag,TRUE
mov drv_swap_flag,FALSE
jz short @@is_true
mov drv_swap_flag,TRUE
@@is_true:
jmp short m_w_boot
m_swap_drives endp
; Menüpunkt Reset Screen
m_reset_scr proc
mov bx,offset reset_scr_txt
call puts
ret
m_reset_scr endp
; Fehler durch Ueberschreiten der DMA Segment Grenze
; siehe c't 4/90 S.412
dma_bound proc
mov bx,STDERR
mov cx,dma_txt_end-dma_txt
mov dx,offset dma_txt
DOS WRITE_TO_HANDLE
mov al,1
jmp ABORT_EMU
dma_bound endp
disp_disk_err proc
pushf ; cy aufheben
PUSHR <si,cx,dx,bx>
PUSHR <DI,BP,ES,DS>
DOS GET_EXTENDED_ERRORS
POPR <DS,ES,BP,DI>
mov bx,offset disk_err_num
call hexbyte
mov al,bh
mov bx,offset disk_err_cls
call hexbyte
mov si,offset disk_err_txt
mov cx,disk_err_txt_end-disk_err_txt
call box_call
POPR <bx,dx,cx,si>
popf
ret
disp_disk_err endp
disp_f_not_found proc
pushf ; cy aufheben
PUSHR <si,cx>
mov si,offset f_not_found_txt
mov cx,f_not_found_txt_end-f_not_found_txt
call box_call
POPR <cx,si>
popf
ret
disp_f_not_found endp
disp_no_mem proc
PUSHR <si,cx>
mov si,offset no_mem_txt
mov cx,no_mem_txt_end-no_mem_txt
call box_call
POPR <cx,si>
ret
disp_no_mem endp
hexbyte PROC
; in: bx-> store for hexbyte
; al=hexbyte
push ax
shr al,1 ;extract high nibble
shr al,1
shr al,1
shr al,1
call nib
pop ax
and al,0fh
nib:
add al,90h ;special hex conversion sequence
daa ;using ADDs and DAA's
adc al,40h
daa ;nibble now converted to ASCII
mov [bx],al
inc bx
ret
hexbyte ENDP
EXTRN EmuMenu_DoMenue:far,EmuMenu_EditDPB:far
EXTRN EmuMenu_InputString:far,EmuMenu_About:far,EmuMenu_init:far
; fmodula2 laedt merkwuerdigerweise am Anfang DS aus CS:0
; also muss man dort ds hinbringen
; Trotzdem sollte vor Aufruf einer Funktion DS richtig
; geladen sein.
patch_box proc
mov ax,DATA
mov [cs:0],ax
ret
patch_box endp
box_call proc ; um Modula 2 UPs aufzurufen
;
; in: ds:si => menuetext
; cx = len(menuetext)
; out: ax = result
;
; auf dem STACK muss GENUG FREI sein, um zu erzeugendes WINDOW
; ABZUSPEICHERN
;
COMMENT @
DEFINITION MODULE EmuM2;
FROM SYSTEM IMPORT BYTE,WORD;
TYPE PhysDiskPars = RECORD
cpm_drive : BYTE;
first_phys_sec : BYTE;
phys_tracks : BYTE;
phys_sec_pt : BYTE;
bytes_per_sec : BYTE;
autologin_flag : BYTE;
END;
dpb = RECORD
spt : WORD;
bsh : BYTE;
blm : BYTE;
exm : BYTE;
dsm : WORD;
drm : WORD;
al0 : BYTE;
al1 : BYTE;
cks : WORD;
off : WORD;
END;
DPBPtr = POINTER TO dpb;
PDPPtr = POINTER TO PhysDiskPars;
PROCEDURE DoMenue(x,y:CARDINAL;messages:ARRAY OF CHAR;
VAR WinSave:ARRAY OF CHAR;VAR res:CARDINAL);
(* Pop Up Menue, obere linke Ecke des Rahmens bei x,y.
messages = String mit darzustellenden Menuepunkten; Stringende = 0C.
Menuepunkte werden durch | getrennt, auch nach letztem Menuepunkt
MUSS | stehen.
Jeder Menuepunkt darf durch Druecken eines einzigen Zeichens angewaehlt
werden, das hell dargestellt wird. Diesem Zeichen muss der Backslash \
vorangestellt werden.
Es duerfen maximal 20 Menuepunkte sein.
WinSave muss genug Platz enthalten, um Hintergrund + dessen Attribute
abzuspeichern, = (Laengster Menuepunkt+2)*2*(Menuepunkte+2)
*)
PROCEDURE EditDPB(p:DPBPtr;q:PDPPtr;
VAR WinSave:ARRAY OF CHAR;VAR OK:BOOLEAN);
(* Len WinSave=20*18*2=720 *)
PROCEDURE InputString(VAR WinSave,s,p:ARRAY OF CHAR);
(* Es wird vorrausgesetzt, daß prompt p < 30 und string s < 30 *)
PROCEDURE About(VAR WinSave:ARRAY OF CHAR);
(* Author ausgeben *)
END EmuM2.
@
HIGH_WinSave=(LONGEST_MESSAGE+2)*2*(MESSAGE_COUNT+2)
IF HIGH_WinSave gt 2*STACK_SIZE/3
%out WARNING: POPUP WINDOW REQUIRES MUCH OF STACK
ENDIF
PUSHR <bx,cx,dx,di,si,bp,ds,es>
sub sp,HIGH_WinSave
mov bx,sp
mov ax,20
push ax ; x
mov ax,7
push ax ; y
push cx ; high(messages)
push ds ; seg(messages)
push si ; offs(messages)
mov cx,HIGH_WinSave
push cx ; high(WinSave)
push ss ; seg(WinSave)
push bx ; offs(WinSave)
mov ax,seg prg_ext_box_res
push ax ; seg(prg_ext_box_res)
mov ax,offset prg_ext_box_res
push ax ; offs(prg_ext_box_res)
;box_adr equ this byte
mov ax,DATA
mov ds,ax
call EmuMenu_DoMenue
add sp,HIGH_WinSave
POPR <es,ds>
mov ax,prg_ext_box_res
POPR <bp,si,di,dx,cx,bx>
ret
box_call endp
edit_disk_pars proc
HIGH_WinSave=800
IF HIGH_WinSave gt 2*STACK_SIZE/3
%out WARNING: POPUP WINDOW REQUIRES MUCH OF STACK
ENDIF
PUSHR <bx,cx,dx,di,si,bp,ds,es>
sub sp,HIGH_WinSave
mov bx,sp
mov ax,z80seg_adr
push ax ; seg(p) p:DPBPtr
mov ax,offset dpb0+ZBIOSDISPL
push ax ; offs(p)
mov ax,seg cpm_drive
push ax ; seg (q) q:PDPPtr;
mov ax,offset cpm_drive
push ax ; offs (q)
mov cx,HIGH_WinSave
push cx ; high(WinSave)
push ss ; seg(WinSave)
push bx ; offs(WinSave)
mov ax,seg good_dpb_edit
push ax ; seg(ok)
mov ax,offset good_dpb_edit
push ax ; offs(ok)
mov ax,DATA
mov ds,ax
call EmuMenu_EditDPB
add sp,HIGH_WinSave
POPR <es,ds,bp,si,di,dx,cx,bx>
ret
edit_disk_pars endp
;PROCEDURE InputString(VAR WinSave,s,p:ARRAY OF CHAR);
input_string proc ; ds:si = prompt, bx = len prompt
; ds:di = string cx = len string
LOCAL prompt_str,lenpr,in_str,lenstr:WORD = AUTO_SIZE
push bp
mov bp,sp
sub sp,AUTO_SIZE
mov prompt_str,si
mov in_str,di
mov lenpr,bx
mov lenstr,cx
HIGH_WinSave=800
IF HIGH_WinSave gt 2*STACK_SIZE/3
%out WARNING: POPUP WINDOW REQUIRES MUCH OF STACK
ENDIF
PUSHR <bx,cx,dx,di,si,bp,ds,es>
sub sp,HIGH_WinSave
mov bx,sp
mov ax,z80seg_adr
mov cx,HIGH_WinSave
push cx ; high(WinSave)
push ss ; seg(WinSave)
push bx ; offs(WinSave)
push lenstr ; high(s)
push ds ; seg(s)
push in_str ; offs(s)
push lenpr ; high(p)
push ds ; seg(p)
push prompt_str ; offs(p)
mov ax,DATA
mov ds,ax
call EmuMenu_InputString
add sp,HIGH_WinSave
POPR <es,ds,bp,si,di,dx,cx,bx>
mov sp,bp
pop bp
ret
input_string endp
load_disk_pars proc
LOCAL handle:WORD = AUTO_SIZE
push bp
mov bp,sp
sub sp,AUTO_SIZE
call @@fopen
jnc short @@ok
cmp ax,2
CALLZ disp_f_not_found
jc short @@exit ; nicht da
@@ok:
call @@fread
call @@fclose
call update_disk_pars
mov autologin_flag,FALSE
@@exit:
mov sp,bp
pop bp
ret
@@fopen:
mov al,0
mov dx,offset fmt_file_name
DOS OPEN_FILE
mov handle,ax
ret
@@fclose:
mov bx,handle
DOS CLOSE_FILE
CALLC disp_disk_err
ret
@@fread:
mov dx,offset tmp_word
mov bx,handle
mov cx,2
DOS READ_FROM_HANDLE
CALLC disp_disk_err
mov bx,dx
mov bx,[bx]
cmp bx,'WJ'
jnz short @@ldexit ; falscher Kenncode <> 'JW'
; cp/m Parameter laden
mov dx,offset offset dpb0+ZBIOSDISPL
mov bx,handle
mov cx,size dpb
push ds
mov ax,z80seg_adr
mov ds,ax
DOS READ_FROM_HANDLE
pop ds
CALLC disp_disk_err
; physikalische Parameter laden
mov dx,offset first_phys_sec
mov bx,handle
mov cx,autologin_flag-first_phys_sec
DOS READ_FROM_HANDLE
CALLC disp_disk_err
@@ldexit:
ret
load_disk_pars endp
save_disk_pars proc
LOCAL handle:WORD = AUTO_SIZE
push bp
mov bp,sp
sub sp,AUTO_SIZE
call @@fopen
jc short @@exit
call @@fwrite
call @@fclose
@@exit:
mov sp,bp
pop bp
ret
@@fopen:
mov cx,0
mov dx,offset fmt_file_name
DOS CREATE_FILE
CALLC disp_disk_err
mov handle,ax
ret
@@fclose:
mov bx,handle
DOS CLOSE_FILE
CALLC disp_disk_err
ret
@@fwrite:
; erst Kennung schreiben
mov bx,offset tmp_word
mov [bx],'WJ'
mov dx,bx
mov bx,handle
mov cx,2
DOS WRITE_TO_HANDLE
CALLC disp_disk_err
; cp/m Parameter schreiben
mov dx,offset offset dpb0+ZBIOSDISPL
mov bx,handle
mov cx,size dpb
push ds
mov ax,z80seg_adr
mov ds,ax
DOS WRITE_TO_HANDLE
pop ds
CALLC disp_disk_err
; physikalische Parameter schreiben
mov dx,offset first_phys_sec
mov bx,handle
mov cx,autologin_flag-first_phys_sec
DOS WRITE_TO_HANDLE
CALLC disp_disk_err
ret
save_disk_pars endp
about_author proc
PUSHR <bx,cx,dx,di,si,bp,ds,es>
sub sp,HIGH_WinSave
mov bx,sp
mov cx,HIGH_WinSave
push cx ; high(WinSave)
push ss ; seg(WinSave)
push bx ; offs(WinSave)
mov ax,DATA
mov ds,ax
call EmuMenu_About
add sp,HIGH_WinSave
POPR <es,ds,bp,si,di,dx,cx,bx>
ret
about_author endp
clearz80 proc ; loesche z80mem
;
; input: ax=seg z80 segment
; alle register ok
;
push es
PUSHR <ax,cx,di>
mov es,ax
mov cx,8000h ; 32k words
mov di,0
mov ax,0
rep stosw ; es:di := 0000h
POPR <di,cx,ax>
pop es
ret
clearz80 endp
dos_exec proc
cmp exec_usr_flg,TRUE
jz short @@cont1
mov al,DOS_SCR_PAGE
call sel_scr_page
mov bx,STDOUT
mov cx,exit_str_end-exit_str
mov dx,offset exit_str
DOS WRITE_TO_HANDLE
@@cont1:
PUSHR <bx,cx,dx,di,si,bp,es>
; first try calling `COMSPEC`
mov sp_save,sp
mov ss_save,ss
mov bx,seg exec_par_block
mov es,bx
mov bx,offset exec_par_block
mov dx,offset comspec
mov ax,seg comspec
mov ds,ax
mov al,0 ; exec
DOS EXEC ; ax:=error code
jnc short @@done
; if that failed try \COMMAND.COM
mov bx,seg exec_par_block
mov es,bx
mov bx,offset exec_par_block
mov dx,offset exec_fname
mov ax,seg exec_fname
mov ds,ax
mov al,0 ; exec
DOS EXEC ; ax:=error code
@@done:
mov bx,emudata_seg
mov ds,bx
cli
mov ss,ss_save
mov sp,sp_save
sti
POPR <es,bp,si,di,dx,cx,bx>
cmp ax,2 ; file not found
jnz short @@cc_found
mov bx,STDERR
mov cx,no_cc_str_end-no_cc_str
mov dx,offset no_cc_str
DOS WRITE_TO_HANDLE
@@press_esc:
call crtin
cmp al,ESC_KEY
jnz short @@press_esc
@@cc_found:
cmp exec_usr_flg,TRUE
mov al,0
CALLNZ sel_scr_page
ret
dos_exec endp
dos_shell proc
LOCAL handle:WORD,mem_adr:WORD = AUTO_SIZE
PUSHR <ax,bx,cx,dx,si,di>
push bp
mov bp,sp
sub sp,AUTO_SIZE
mov ax,ovl_base
sub ax,z80seg_adr
jnc short @@memok
call disp_no_mem
jmp short @@exit
@@memok:
call @@fcreate
jc short @@exit
call @@fwrite
call @@fclose
call @@set_free
call dos_exec
call @@get_mem
call @@fopen
call @@fread
call @@fclose
mov dx,offset temppath
DOS DELETE_FILE
CALLC disp_disk_err
@@exit:
mov sp,bp
pop bp
POPR <di,si,dx,cx,bx,ax>
ret
@@set_free:
push es
mov es,psp_adr
mov bx,prog_len
sub bx,2000h
DOS MODIFY_MEMORY
mov bx,mem_end
sub bx,2000h
mov ovl_base,bx
pop es
ret
@@get_mem:
push es
mov es,psp_adr
mov bx,prog_len
DOS MODIFY_MEMORY
pop es
ret
@@fcreate:
DOS GET_DISK_DRIVE
add al,'A'
mov byte ptr temppath,al
mov dl,0 ; default drive
mov si,(offset temppath) + 3
DOS GET_DIRECTORY
dec si
push si
@@srch0:
lodsb
or al,al
jnz short @@srch0
sub si,2 ; si -> last char
cmp byte ptr [si],'\'
jz short @@root
inc si
mov byte ptr [si],'\'
@@root:
inc si
mov di,si
push ds
pop es
mov si,offset temp_fname
call strcpy
pop dx ; dx -> "\..\.."
mov cx,2 ; Hidden
DOS CREATE_FILE
CALLC disp_disk_err ; Abbruch nach Error in toplevel
mov handle,ax
ret
@@fopen:
mov al,0
mov dx,offset temppath
DOS OPEN_FILE
CALLC disp_disk_err
mov handle,ax
ret
@@fclose:
mov bx,handle
DOS CLOSE_FILE
CALLC disp_disk_err
ret
@@fwrite:
push ds
mov si,ovl_base
mov cx,4 ; do 4*32K
@@wlp:
push cx
mov bx,handle
mov cx,8000h
mov ds,si
mov dx,0
DOS WRITE_TO_HANDLE
CALLC disp_disk_err
add si,800h
pop cx
loop @@wlp
pop ds
ret
; end fwrite
@@fread:
push ds
mov si,ovl_base
mov cx,4 ; do 4*32K
@@rlp:
push cx
mov bx,handle
mov cx,8000h
mov ds,si
mov dx,0
DOS READ_FROM_HANDLE
CALLC disp_disk_err
add si,800h
pop cx
loop @@rlp
pop ds
ret
; end fread
dos_shell endp
if HARD_DISC_TOO
create_harddisc proc
call @@fopen
jc short @@exit
; we use secbuf as buffer, therefore it's contents must
; be invalidated
invalidate_rdbuf
; fill a buffer of 1K with empty byte
push es
push ds
pop es
mov cx,1024/2
mov ax,0e5e5h
mov di,offset secbuf
rep stosw
pop es
mov cx,(DRMH+1)*32/1024 ; size of directory in K
@@wrloop:
push cx
mov cx,1024
mov dx,offset secbuf
mov bx,handle
DOS WRITE_TO_HANDLE
CALLC disp_disk_err
pop cx
loop @@wrloop
call @@fclose
@@exit:
ret
@@fopen:
mov cx,0
mov dx,offset harddisc_fname
DOS CREATE_FILE
CALLC disp_disk_err
mov handle,ax
ret
@@fclose:
mov bx,handle
DOS CLOSE_FILE
CALLC disp_disk_err
ret
create_harddisc endp
endif
if HARD_DISC_TOO
init_harddisc proc
PUSHR <ax,bx,cx,dx,si,di>
cmp new_hdsk_flg,TRUE ; /C
CALLZ create_harddisc
mov al,2 ; read/write
mov dx,offset harddisc_fname
DOS OPEN_FILE
jc short @@exit ; not found
mov hd_handle,ax ; also flag for hd present
@@exit:
POPR <di,si,dx,cx,bx,ax>
ret
init_harddisc endp
endif
init_ramdisc proc
push es
PUSHR <ax,cx>
mov ax,ramdisklen ; in paragraphen
LOG2(64) ; nach cl (1024/16)
shr ax,cl ; ax:=ramdisk len in KB
and ax,0fff0h or 1100b ; um Vielfache von 4K zu bekommen
cmp ax,max_rd_size
jna short @@rd_too_big
mov ax,max_rd_size
@@rd_too_big:
push ax
LOG2(4)
shr ax,cl ; ax/=4
mov lasttrack_rd,al ; Tracks zu 4 K
pop ax
; dsm=ramdisklen in K/2 -1 , da bls=2k
shr ax,1 ; geht klar, da sowieso vielfaches von 4
dec ax
cmp ax,255
jna short @@nobig
mov ax,255 ; mehr wie 510k geht im PC sowieso kaum
@@nobig:
mov dpb1.dsm,ax ; blockzahl
inc ax ; da KB=(dsm+1)*2
shl ax,1
mov ramdisklen,ax ; KBzahl
mov dpb1.bsh,4
mov dpb1.blm,15
mov dpb1.exm,1 ; blocksize=2048
mov dpb1.drm,127 ; dir entries
mov dpb1.al0,11000000b
; loesche ramdisc
mov ax,rdseg_adr
mov es,ax
mov ax,ramdisklen
@@clrloop:
cmp ax,64 ; k
jb short @@less64
push ax
mov cx,8000h ; do 32k words
mov di,0
mov ax,0e5e5h
rep stosw ; es:di := E5E5
mov ax,es
add ax,1000h
mov es,ax
pop ax
sub ax,64
jmp @@clrloop
@@less64:
LOG2(512)
shl ax,cl ; ax*=1024/2 (word)
mov cx,ax
mov di,0
mov ax,0e5e5h
rep stosw ; es:di := E5E5
POPR <cx,ax>
pop es
call load_ramdisc
ret
init_ramdisc endp
; speichere Ramdisk auf Platte
;
; der LOCAL Befehl legt lokale Auto-Vars auf dem Stack an mit
; Laenge AUTO_SIZE
;
save_ramdisc proc
LOCAL handle:WORD = AUTO_SIZE
push bp
mov bp,sp
sub sp,AUTO_SIZE
call @@fopen
jc short @@exit
call @@fwrite
call @@fclose
@@exit:
mov sp,bp
pop bp
ret
@@fopen:
mov cx,0
mov dx,offset ramdisc_fname
DOS CREATE_FILE
CALLC disp_disk_err
mov handle,ax
ret
@@fclose:
mov bx,handle
DOS CLOSE_FILE
CALLC disp_disk_err
ret
@@fwrite:
; erst mal Ramdiskgroesse schreiben, beim Laden muss sie stimmen
mov bx,offset secbuf
mov ax,ramdisklen
mov [bx],ax
mov dx,bx
mov bx,handle
mov cx,2
DOS WRITE_TO_HANDLE
CALLC disp_disk_err
mov bx,rdseg_adr
mov ax,ramdisklen
@@wrtloop:
cmp ax,0 ; fertig ?
; 0 oder negativ = Abbruch
jle short @@wrtexit
push ax
cmp ax,32 ; kilobyte
mov cx,8000h ; 32k
ja short @@more32
LOG2(1024) ; nach cl
shl ax,cl ; ax*=1024
mov cx,ax ; cx = Laenge in Byte
@@more32:
push bx
push ds
push bx
pop ds
mov bx,handle ; klappt, da ueber SS:BP
mov dx,0 ; offset
DOS WRITE_TO_HANDLE
CALLC disp_disk_err
pop ds
pop bx
pop ax
add bx,800h ; segment um 32k weitersetzen
sub ax,32 ; restlaenge -=32
jmp @@wrtloop
@@wrtexit:
ret
; end fwrite
save_ramdisc endp
; hole Ramdisk von Platte
;
; der LOCAL Befehl legt lokale Auto-Vars auf dem Stack an mit
; Laenge AUTO_SIZE
;
load_ramdisc proc
LOCAL handle:WORD = AUTO_SIZE
push bp
mov bp,sp
sub sp,AUTO_SIZE
call @@fopen
jc short @@exit ; nicht da
call @@fread
call @@fclose
@@exit:
mov sp,bp
pop bp
ret
@@fopen:
mov al,0
mov dx,offset ramdisc_fname
DOS OPEN_FILE
mov handle,ax
ret
@@fclose:
mov bx,handle
DOS CLOSE_FILE
CALLC disp_disk_err
ret
@@fread:
mov dx,offset secbuf
mov bx,handle
mov cx,2
DOS READ_FROM_HANDLE
CALLC disp_disk_err
mov bx,dx
mov bx,[bx]
cmp bx,ramdisklen
ja short @@rdexit ; ueberhaupt zu gross
mov ax,bx ; Laenge Ramdisk auf Platte
mov bx,rdseg_adr
@@rdloop:
cmp ax,0 ; fertig ?
jle short @@rdexit
push ax
cmp ax,32
mov cx,8000h ; 32k
ja short @@more32
LOG2(1024) ; nach cl
shl ax,cl ; ax*=1024
mov cx,ax ; cx = Laenge in Byte
@@more32:
push bx
push ds
push bx
pop ds
mov bx,handle ; klappt, da ueber SS:BP
mov dx,0 ; offset
DOS READ_FROM_HANDLE
CALLC disp_disk_err
pop ds
pop bx
pop ax
add bx,800h ; segment um 32k weitersetzen
sub ax,32 ; restlaenge -=32
jmp short @@rdloop
@@rdexit:
ret
; end fread
load_ramdisc endp
in_emu_flg db FALSE
count1 db 0
count2 db 0
bios88 proc
;
; bios88 ist handler fuer alle bios calls
; In: al=bios nummer (warm boot = 1)
; cx,bx,dx wie bei cp/m bios calls
; Out: di,ah zerstoert
; si kann veraendert sein, falls bios change_si_flag
; hat nach wboot
;
; waehrend aller bios routinen zeigt ds auf emudata_seg
; es z80cpu_seg
mov cs:in_emu_flg,FALSE
mov di,emudata_seg
mov ds,di
mov z80_pc,si
cmp breakflag,TRUE
CALLZ prg_exit
PUSHR <bp,dx>
; cx nicht pushen, da cl:=drive # bei wboot
cmp al,30
jna short @@goodfn
mov al,1 ; illegal function -> warmboot
@@goodfn:
xor ah,ah ; bios # = index
sal ax,1
mov di,ax
call cs:biostab[di]
mov si,z80_pc
POPR <dx,bp>
cmp breakflag,TRUE
CALLZ prg_exit
mov di,z80seg_adr
mov ds,di
mov es,di
mov cs:in_emu_flg,TRUE
jmp bios88ret
bios88 endp
biostab label word
dw boot ; cold start
dw wboot ; warm start
dw constat ; console status
dw conin ; console character in
dw conout ; console character out
dw list ; list character out
dw punch ; punch character out
dw reader ; reader character out
dw home ; move head to home position
dw seldsk ; select disk
dw settrk ; set track number
dw setsec ; set sector number
dw setdma ; set dma address
dw read ; read disk
dw write ; write disk
dw listst ; return list status
dw sectran ; sector translate
rept 25-16-1
dw wboot ; not used
endm
dw cpm3_move ; move
dw cpm_get_time ; time
rept 30-26-1
dw wboot ; not used
endm
dw userfn ; here: used for data transfer dos/cpm
;
;
; individuelle UPs um jede Funktion auszufuehren
;
boot proc
;simplest case is to just perform parameter initialization
mov in_boot_flag,TRUE
mov bx,offset on_sign_string
call puts
call instzbios
cmp fmt_file_flg,TRUE
CALLZ load_disk_pars
call read_sys ; lade bdos aus file CPMSYS.CPM
jnc short @@goodload ; laden aus File klappte
call get_sys ; BDOS mit BIOS fkt von Diskette laden
@@goodload:
; Copyright von dr ausgeben
mov cl,27
call crtout
mov cl,'k' ; pop cursor pos
call crtout
mov cl,27
call crtout
mov cl,'J' ; Rest vom Fenster loeschen
call crtout
mov bx,offset cpm_bdos_buf+18h
call puts
mov bx,offset cr_lf_txt
call puts
;
; berechne adresse von ccp,bdos und bios
; dabei wird die JMP CCPENTRY Anweisung bei CCP+0 benutzt
;
mov di,offset cpm_bdos_buf+807h
mov al,[di]
cmp al,11h
jnz short @@no_standard
mov di,offset cpm_bdos_buf+1
mov ax,[di]
sub ax,35ch ; ax:=start ccp
; CP/M CCP? dann al jetzt 0
or al,al
jz short @@is_standard
; sonst defaultmaessig e400 annehmen
@@no_standard:
mov ax,0e400h
@@is_standard:
mov ccp_adr,ax
add ax,806h
mov bdos_adr,ax
add ax,(BDOSLEN-806h)
mov bios_adr,ax
call reset_crt
call init_iobyte
mov di,CDISK
mov byte ptr es:[di],0 ; select disk zero
call reset_punch_buf
call reset_reader_buf
mov in_boot_flag,FALSE
jmp wboot
boot endp
init_iobyte proc
mov di,IOBYTE
mov byte ptr es:[di],DEFAULT_IOBYTE
ret
init_iobyte endp
;
; setze Punch buffer auf leer
;
reset_punch_buf proc
mov punch_buf_entries,0
mov punch_buf_ptr,offset punch_buf
ret
reset_punch_buf endp
;
; setze Reader buffer auf nicht belegt
;
reset_reader_buf proc
mov reader_empty_flag,FALSE ; noch nicht lesen versucht
mov reader_buf_entries,0
mov reader_buf_ptr,offset reader_buf
mov reader_file_pointer,0
mov reader_file_pointer+2,0
ret
reset_reader_buf endp
;
; Stringausgabe
; In: ds:bx zeigt auf String, Stringende = \0
;
puts proc
@@loop:
mov cl,[bx]
or cl,cl
jz short @@exit
inc bx
push bx
call crtout
pop bx
jmp short @@loop
@@exit:ret
puts endp
;
; In: ds:si = source, es:di = target
; copy string-zero
;
strcpy proc
push ax
@@loop:
lodsb
stosb
or al,al
jnz short @@loop
pop ax
ret
strcpy endp
instzbios proc
;
; installiere z80 bios
; und discparameter
;
PUSHR <si,di,cx>
mov si,offset zbiosbeg
mov di,ZBIOS
mov cx,zbiosend-zbiosbeg
cld
rep movsb ; es:di := ds:si
POPR <cx,di,si>
ret
instzbios endp
instjp proc
;
; installiere z80 bios Sprungleiste bei bios_adr
;
; register o.k.
;
; darf nicht aufgerufen werden, bevor bios_adr
; von boot aufgesetzt wurde
;
PUSHR <si,di,cx>
mov si,offset wboote-3
mov di,bios_adr
mov cx,zvecend-wboote+3
cld
rep movsb ; es:di := ds:si
POPR <cx,di,si>
ret
instjp endp
read_sys proc
;
; lade cp/m bdos aus file CPMSYS.CPM in buffer
; um es durch warm boot zu benutzen
;
; das File entspricht dem durch MOVCPM erzeugten Speicherauszug
; dabei beginnt das eingentlich System erst ab Offset 880h
MOVCPM_OFFS = 880H
PUSHR <ax,bx,cx,dx,si>
mov al,0
mov dx,offset cpmsys_fileName
DOS OPEN_FILE
jc short @@exit
mov si,ax
; setze Filepointer auf CCP Anfang im File
mov cx,0 ; cx:dx: offset
mov dx,MOVCPM_OFFS
mov al,0 ; offset from start
mov bx,si
DOS MOVE_FILE_POINTER
;
; emudata_seg:buffer := CPMSYS.CPM
;
mov cx,BDOSLEN ; Laenge
mov bx,si ; handle
mov dx,offset cpm_bdos_buf
DOS READ_FROM_HANDLE
jc short @@exit
mov bx,si
DOS CLOSE_FILE
CALLC disp_disk_err
@@exit:
POPR <si,dx,cx,bx,ax>
ret
read_sys endp
; System von SystemSpuren laden
; while sector in systemsectoren do
; lies sector
; if an Adresse String COPYRIGHT 1979 ..
; sys=naechste SYS_SECS sektoren
; lies sys
; exit
; mache Checksumme ueber sys
; if falsch
; "no sys"
; endif
; endif
; endwhile
; "no sys"
get_sys proc
LOCAL sys_recs,last_sys_rec:WORD = AUTO_SIZE
push bp
mov bp,sp
sub sp,AUTO_SIZE
@@get_sys_loop:
call login_disc
call home ; else dir track will not be read
mov ah,0 ; take drive A: = floppy disc
mov al,dpb0.off-dpb0.spt ; off
call get_dpb_entrie ; nach bx
push bx
mov ah,0 ; A:
mov al,dpb0.spt-dpb0.spt ; spt
call get_dpb_entrie ; nach bx
pop ax
mul bx ; ax:=system records
mov sys_recs,ax
mov cx,0
; im Systembereich nach String "COPYRIGHT (C) 1979 " suchen
@@srch_loop:
cmp cx,sys_recs
jnc short @@read_err ; schon ausserhalb sys
push cx ; sector
push cx
mov cx,DEFAULT_DMA
call setdma
pop cx
mov ah,0 ; A:
call set_abs_sect
call read
pop cx
cmp al,1
jz short @@read_err
call @@compare
jz short @@found
inc cx
jnz @@srch_loop
@@found:
mov bx,DEFAULT_DMA
push cx
add cx,SYS_SECS
mov last_sys_rec,cx
pop cx
@@loop:
push cx ; sector
push bx ; dma
push cx
mov cx,bx
call setdma
pop cx
mov ah,0 ; take drive A: = floppy disc
call set_abs_sect
call read
pop bx
pop cx
cmp al,1
jz short @@read_err
add bx,RECORD_LEN
inc cx
cmp cx,last_sys_rec
jnz @@loop
jmp short @@good_load
@@read_err:
mov bx,offset no_sys
call puts
call crtin
jmp @@get_sys_loop
@@good_load:
; System aus CP/M Segment nach Datensegment uebertragen
PUSHR <si,di,bx,cx,ax>
push ds
push es
mov di,offset cpm_bdos_buf
mov si,DEFAULT_DMA
mov ax,z80seg_adr
mov ds,ax
mov ax,emudata_seg
mov es,ax
mov cx,BDOSLEN/2
cld
rep movsw ; es:di := ds:si
pop es
pop ds
POPR <ax,cx,bx,di,si>
mov sp,bp
pop bp
ret
@@compare:
PUSHR <si,di,cx,ax>
mov si,offset bdos_str
mov di,DEFAULT_DMA+10H
mov cx,bdos_str_end-bdos_str
@@cp_loop:
mov al,[es:di]
mov ah,[si]
inc si
inc di
cmp al,ah
jnz short @@cp_exit
loop @@cp_loop
@@cp_exit:
POPR <ax,cx,di,si>
ret
get_sys endp
;
set_abs_sect proc ; absoluten Sektor in cx, drive ah setzen
push cx
mov al,0 ; spt
call get_dpb_entrie ; nach bx
pop ax
mov dx,0
div bx
push dx
mov cx,ax
call settrk
pop cx
call setsec
ret
set_abs_sect endp
wboot proc
push di
call copy_bdos
call instjp ; bios Sprungleiste installieren
;
; 0000h := JP WBOOT
; 0005h := JP BDOS
;
mov di,0 ; start cp/m memory
mov byte ptr es:[di+0],0c3h
mov bx,bios_adr
add bx,3 ; bx:=warmboot
mov es:[di+1],bx ; set address field for jmp at 0
;
mov byte ptr es:[di+5],0c3h
mov bx,bdos_adr ; bdos entry point
mov es:[di+6],bx ; address field of jump at 5 to bdos
;
mov cx,DEFAULT_DMA ; default dma address is 80h
call setdma
call login_disc ; physikalisches Login
;
mov ax,ccp_adr
mov z80_pc,ax ; jp ccp
mov di,CDISK
mov cl,es:[di] ; get current disk number & send to ccp
pop di
ret ;go to cp/m for further processing
copy_bdos:
PUSHR <si,di,cx>
mov di,ccp_adr
mov si,offset cpm_bdos_buf
mov cx,BDOSLEN/2
rep movsw ; es:di := ds:si
POPR <cx,di,si>
ret
wboot endp
;
;
;
constat proc
;
; console status, return 0ffh if character ready, 00h,Z_flag if not
;
; Out: al=status
;
mov bx,IOBYTE
mov bl,es:[bx]
and bx,11b
add bx,bx
jmp cs:consttab[bx]
consttab label word
dw offset crtinstat
dw offset crtinstat
dw offset readstat
dw offset crtinstat
constat endp
;
;
conin proc ; CON:
;
; Out: al=char
;
mov bx,IOBYTE
mov bl,es:[bx]
and bx,11b
add bx,bx
jmp cs:conintab[bx]
conintab label word
; -> consttab
dw offset crtin ; TTY:
dw offset crtin ; CRT:
dw offset reader ; BAT:
dw offset crtin ; UC1:
conin endp
conout proc ; CON:
;
; In: c=char
;
mov bx,IOBYTE
mov bl,es:[bx]
and bx,11b
add bx,bx
mov al,1
jmp cs:conouttab[bx]
conouttab label word
dw offset crtout ; TTY:
dw offset crtout ; CRT:
dw offset punch ; BAT:
dw offset crtout ; UC1:
conout endp
;
; test auf ^C, wenn ja, warm boot
;
tst_ctrl_c proc
push ax
call constat
jz short @@exit
call crtin
cmp al,3 ; ^C
clc ; cy = no error
jnz short @@exit
mov bx,offset control_c_txt
call puts
stc
@@exit:
pop ax
ret
tst_ctrl_c endp
list proc ; LST:
mov bx,IOBYTE
mov bl,es:[bx]
and bx,11000000b
shr bx,6-1
jmp cs:lsttab[bx]
lsttab label word
dw offset crtout ; TTY:
dw offset crtout ; CRT:
dw offset lpt0list ; LPT:
dw offset lpt1list ; UL1:
list endp
lpt0list proc
xor dx,dx
jmp short lptlist
lpt0list endp
lpt1list proc
mov dx,1
jmp short lptlist
lpt1list endp
;
; list character in register c auf Drucker
;
lptlist proc
call listst
jnz short @@ok
call tst_ctrl_c
jnc short lptlist
mov z80_pc,0 ; Z80 macht ab Adresse 0 weiter
ret
@@ok:
mov al,cl ; char nach register a
xor ah,ah ; list character
push bp
int 17h
pop bp
cmp ah,10h ; ok
jnz short lptlist
ret
lptlist endp
;
; return list status (0,Z_flag if not ready, 1 if ready)
;
listst proc
push dx
mov ah,2 ; test printer
int 17h
mov al,1
cmp ah,90h ; on line und not busy
jz short @@lret
dec al
@@lret:
or al,al
pop dx
ret
listst endp
com0list proc
xor dx,dx
jmp short comlist
com0list endp
com1list proc
mov dx,1
jmp short comlist
com1list endp
comlist proc
mov ah,1
mov al,cl
int 14h
ret
comlist endp
com0read proc
xor dx,dx
jmp short comread
com0read endp
com1read proc
mov dx,1
jmp short comread
com1read endp
comread proc
mov ah,2
mov al,cl
int 14h
ret
comread endp
date_dos_2_cpm proc ; out: AX=tage seit 1.Jan. 1978
push bx
push cx
push dx
DOS GET_DATE
push dx
mov bx,1978
inc cx ; ein Jahr zuviel, für Test ob akt. Schaltjahr
MOV AX,0
@@add_loop:
cmp bx,cx ; aktuelles Jahr erreicht ?
jz short @@years_done
mov dx,bx
and dx,11b ; 0 => durch 4 teilbar
sub dx,1 ; 0 => cy
mov dx,365
adc dx,0 ; wird unten zum abziehen gebraucht
add ax,dx ; 0 => 366
inc bx
jmp @@add_loop
@@years_done:
sub ax,dx ; da ein Jahr oben zuviel, 365 oder 366 wieder weg
pop dx ; mon/tag
mov ch,0
mov cl,dl
add ax,cx ; tag
mov cl,dh ; monat
dec cl ; da aktueller Monat schon dran
jz short @@is_jan
mov bx,offset month_tab
@@add_months:
mov dl,[bx] ; verschieden lange monate
inc bx
mov dh,0
add ax,dx
loop @@add_months
@@is_jan:
pop dx
pop cx
pop bx
ret
date_dos_2_cpm endp
cpm3_move proc ; move wie bei cpm plus bios
push ds
push es
pop ds
mov si,bx
mov di,dx
add bx,cx ; hl=blockende
cmp bx,dx ; größer Ziel ?
jnb short @@topdown
cld
rep movsb
jmp short @@exit
@@topdown:
add si,cx
add di,cx
mov bx,si
mov dx,di
dec si
dec di
std
rep movsb
pop ds
ret
@@exit:
mov bx,si
mov dx,di
pop ds
ret
cpm3_move endp
cpm_get_time proc ; dos zeit nach cpm plus zeit
PUSHR <AX,CX,DX>
cmp cl,0
jnz short @@exit ; set time ignorieren
call date_dos_2_cpm
push ds
push es
pop ds
mov @date+ZBIOSDISPL,ax
DOS GET_TIME
mov dl,10
mov al,cl
mov ah,0 ; sonst divide overflow
div dl
mov cl,4
shl al,cl
or al,ah
mov @min+ZBIOSDISPL,al
mov al,ch
mov ah,0
div dl
shl al,cl
or al,ah
mov @hour+ZBIOSDISPL,al
mov al,dh
mov ah,0
div dl
shl al,cl
or al,ah
mov @sec+ZBIOSDISPL,al
pop ds
mov bx,(offset @date)+ZBIOSDISPL
@@exit:
POPR <DX,CX,AX>
ret
cpm_get_time endp
del_punch_file proc
mov dx,offset punch_fileName
DOS DELETE_FILE
ret
del_punch_file endp
; on entry to all userfunctions:
; c = proc number
; de = entry data
; hl = pointer to variable, where to store return value
userfn proc
cmp cl,10
ja wboot ; illegal function
mov ch,0
mov si,cx
add si,si
push bx ; hl
call cs:userfntab[si]
pop di
xchg ax,bx
stosw ; es:di:=ax
mov ax,bx
ret
userfntab label word
dw versionfn ; return version NR
dw openout ; open out to dos
dw closeout ; close out to dos
dw doswrite ; write record to dos
dw dosexec ; execute dos command
dw setfname ; set filename for getnextname ("*.c")
dw getnextname ; return next suitable name
dw openin ; open in dosfile
dw dosread ; read next record from dos
dw closein ; abandon dos file
dw get_phys_drv ; return physical drive number
; don't forget to adapt test at start of userfn
userfn endp
; return physical drive number
; out: al = physical drive number
get_phys_drv proc
mov al,cpm_drive
mov ah,0
ret
get_phys_drv endp
; execute dos command
; in: DE-> zero terminated command line
; out: -
dosexec proc
mov si,dx
mov di,offset usr_lne
mov cx,80 ; in order not to copy too much
@@loop:
mov al,es:[si]
cmp al,0
jz short @@copied
mov ds:[di],al
inc si
inc di
loop @@loop
@@copied:
mov byte ptr ds:[di],13
mov al,83
sub al,cl
mov byte ptr exec_usr_lne,al
mov exec_usr_flg,TRUE
mov [exec_par_block+2],offset exec_usr_lne
mov di,offset exec_par_block
call dos_shell
mov [exec_par_block+2],offset exec_cmd_line
mov exec_usr_flg,FALSE
mov al,0
ret
dosexec endp
; return version NR
versionfn proc
mov ax,41751 ; identification of ZSIM
mov bx,CVERSNR
ret
versionfn endp
; open out file to dos
; in: DE-> zero terminated filename
; out: if error A=1 else 0
; first try to close an old file
openout proc
push dx
call closeout
pop dx
push ds
mov ax,es
mov ds,ax ; ds-> z80cpu seg
mov cl,0 ; write only
DOS CREATE_FILE
CALLC disp_disk_err
pop ds
mov openout_handle,ax
mov al,0
adc al,0 ; error -> 1 else 0
ret
openout endp
; close outfile
; in: -
; out: if error A=1 else 0
closeout proc
mov bx,openout_handle
mov openout_handle,0
cmp bx,0
stc
jz short @@exit
DOS CLOSE_FILE
CALLC disp_disk_err
@@exit:
mov al,0
adc al,0
ret
closeout endp
; write 128 bytes to dos
; in: DE -> data
; out: if error A=1 else 0
doswrite proc
push ds
mov bx,openout_handle
mov ax,es
mov ds,ax ; ds-> z80cpu seg
mov cx,128
DOS WRITE_TO_HANDLE
CALLC disp_disk_err
pop ds
mov al,0
adc al,0
ret
doswrite endp
; set filename for getnextname ("*.c\0")
; in: DE -> fname
; out: if error A=1 else 0
setfname proc
mov cx,80
mov di,offset dosfilespec
mov si,dx
@@loop:
mov al,es:[si]
mov ds:[di],al
or al,al
jz short @@done
inc si
inc di
loop @@loop
mov al,1 ; Name is too long
@@done: ; A is 0
mov new_dosfilespecflg,TRUE
ret
setfname endp
; return next suitable name
; in: DE -> buffer to store filename
; out: if error A=1 else 0
; if found copy filename to buffer
getnextname proc
push dx
push ds
lds dx,default_dta
DOS SET_DTA
pop ds
TSTNCLR new_dosfilespecflg
mov cx,0
mov dx,offset dosfilespec
jz short @@open_next
DOS FIND_FIRST
jmp short @@cont
@@open_next:
DOS FIND_NEXT
@@cont:
pop di
mov al,1
jc short @@exit
push ds
lds si,default_dta
add si,1eh ; si-> file name in dta returned
@@loop:
lodsb ; copy filename
stosb
or al,al ; \0 ?
jnz @@loop
pop ds
@@exit:
ret
getnextname endp
; open in dosfile
; in: DE -> filename\0
; out: if error A=1 else 0
; first try to close an old file
openin proc
push dx
call closein
pop dx
push ds
mov ax,es
mov ds,ax
mov al,0
DOS OPEN_FILE
pop ds
mov openin_handle,ax
mov al,0
adc al,0
ret
openin endp
; read next record from dos
; in: DE -> buffer
; out: if error A=1 else 0;HL=length (1..128)
dosread proc
mov bx,openin_handle
mov cx,128
push ds
mov ax,es
mov ds,ax
DOS READ_FROM_HANDLE
pop ds
mov bx,ax ; length actually read
mov al,0
adc al,0
ret
dosread endp
; abandon dos file
; in: -
; out: if error A=1 else 0
; is called before every openin
closein proc
mov bx,openin_handle
mov openin_handle,0
cmp bx,0
stc
jz short @@exit
DOS CLOSE_FILE
@@exit:
mov al,0
adc al,0
ret
closein endp
;
; punch character in register c
; wird durch Output in File auf MsDos Disk ersetzt
; Buffer wird auf File geschrieben, falls er voll ist
; und auch am Ende des Emulator-
; laufs
;
punch proc ; PUN:
mov bx,IOBYTE
mov bl,es:[bx]
and bx,110000b
shr bx,4-1
jmp cs:puntab[bx]
puntab label word
dw offset filepunch ; TTY:
dw offset filepunch ; PTP:
dw offset com0list ; UP1:
dw offset com1list ; UP2:
punch endp
filepunch proc
;
; fill buffer until full or eof
; open file
; if not exist file
; create file
; endif
; go to end of file
; append character
; close file
; empty buffer
;
; falls punch von bios88 aufgerufen wurde, ist al<>0
; nach Aufruf von wrt_out_punch aber al=0
PUSHR <ax,bx,cx,dx,si>
cmp al,0
jz short @@write_out
mov bx,punch_buf_ptr
mov [bx],cl ; buf:=character
inc bx
mov punch_buf_ptr,bx
inc punch_buf_entries
cmp punch_buf_entries,PUNCH_BUF_SIZE
jnz short @@exit
@@write_out:
mov al,1
mov dx,offset punch_fileName
DOS OPEN_FILE
jnc short @@file_found
mov cl,0 ; write only
DOS CREATE_FILE
CALLC disp_disk_err
@@file_found:
mov bx,ax ; handle
mov cx,0
mov dx,0
mov al,2 ; offset from end
DOS MOVE_FILE_POINTER
mov cx,punch_buf_entries
mov dx,offset punch_buf
DOS WRITE_TO_HANDLE
CALLC disp_disk_err
DOS CLOSE_FILE
call reset_punch_buf
@@exit:
POPR <si,dx,cx,bx,ax>
ret
filepunch endp
wrt_out_punch proc
cmp punch_buf_entries,0
jz short @@exit
xor al,al
call punch
@@exit:
ret
wrt_out_punch endp
;
; RDR:
reader proc ;read character into register a from reader device
mov bx,IOBYTE
mov bl,es:[bx]
and bx,1100b
shr bx,2-1
jmp cs:rdrtab[bx]
rdrtab label word
dw offset filereader ; TTY:
dw offset filereader ; PTR:
dw offset com0read ; UR1:
dw offset com1read ; UR2:
reader endp
filereader proc
; if buffer empty
; open file
; if file not present
; exit
; else
; fill buffer
; endif
; endif
; read char
cmp reader_empty_flag,TRUE
jz short @@empty
cmp reader_buf_entries,0
jnz short @@do_read
; Buffer fuellen
mov al,0
mov dx,offset reader_fileName
DOS OPEN_FILE
jc short @@empty
push ax
mov bx,ax
mov dx,reader_file_pointer
mov cx,reader_file_pointer+2
PUSHR <dx,cx>
add dx,READER_BUF_SIZE
adc cx,0
mov reader_file_pointer,dx
mov reader_file_pointer+2,cx
POPR <cx,dx>
mov al,0 ; offset from start
DOS MOVE_FILE_POINTER
pop bx ; handle
mov cx,READER_BUF_SIZE
mov dx,offset reader_buf
DOS READ_FROM_HANDLE
push ax
DOS CLOSE_FILE
pop ax
cmp ax,0
jz short @@empty
; buffer erfolgreich gefuellt
mov reader_empty_flag,FALSE
mov reader_buf_entries,ax
mov reader_buf_ptr,offset reader_buf
@@do_read:
mov bx,reader_buf_ptr
mov al,[bx]
inc bx
mov reader_buf_ptr,bx
dec reader_buf_entries
jmp short @@exit
@@empty:
mov reader_empty_flag,TRUE
mov al,CPM_EOF
mov bx,0ffffh ; hl=FFFF == Dos File Ende
ret
@@exit:
mov bx,0 ; a=hl=gelesenes byte
mov bl,al
ret
filereader endp
readstat proc
mov al,0
; the following is commented out because else
; ccp cancels operations if input is read from bat
; and al=ff
; cmp reader_empty_flag,TRUE
; jz short @@exit
; dec al
@@exit:
ret
readstat endp
; In: ah=drive
; al=offset im dpb
; Out: bx=word dpb[al]
get_dpb_entrie proc
push si
mov bl,ah
call get_dph_adr ; bx:=dph
xor ah,ah
mov si,ax
mov bx,[es:bx+10] ; bx:=dpb
mov bx,[es:bx+si] ; bx:=dpb[ax]
pop si
ret
get_dpb_entrie endp
;
; select disk in register c
;
seldsk proc
cmp cl,NDISKS-1
mov bx,0000h ; 0 = Drive nicht existent
ja short @@exit
if HARD_DISC_TOO
cmp cl,HARD_DISC
jnz short @@no_hd
cmp hd_handle,0
mov bx,0
jz short @@exit
jmp short @@noswp
@@no_hd:
endif
cmp drv_swap_flag,TRUE
jnz short @@noswp
mov bl,cl
mov cl,1
sub cl,bl
@@noswp:
mov diskno,cl
mov bl,cl
call get_dph_adr
@@exit:
ret
seldsk endp
; In: bl:=Drive #
; Out: bx:= disk parameter header address
;
get_dph_adr proc
mov bh,0 ; high order zero
push cx
LOG2(16) ; nach cl
sal bx,cl ; bx*=16 (size of each header)
pop cx
add bx,offset dph_base+ZBIOSDISPL
ret ;hl=.dph_base(diskno*16)
get_dph_adr endp
;
; move to the track 00 position of current drive
; translate this call into a settrk call with parameter 00
;
home proc
mov cx,0 ; select track 0
call settrk
mov home_flag,TRUE ; zur Auswertung durch read_track
ret
home endp
;
; set track given by register bc
;
settrk proc
mov word ptr track,cx
ret
settrk endp
;
; set sector given by register bc
;
setsec proc
mov word ptr sector,cx
ret
setsec endp
;
;translate the sector given by bc using the
;translate table given by de
;
sectran proc
mov bx,cx
or dx,dx ; table ptr == 0 ?
jz short @@exit
add cx,cx ; index *= 2
mov bx,dx ; hl=trans
add bx,cx
mov bx,[es:bx] ;hl=trans[sector]
@@exit:
ret ;with value in hl
sectran endp
;
; set dma address given by registers b und c
;
setdma proc
mov dmaad,cx
ret
setdma endp
calcadr proc
;
; ax:bx := record adresse in Ramdisk
; illegal sec: cy
;
; 1. 64k track 0..15
; 2. 64k track 16..31
; ..
mov al,track
cmp al,lasttrack_rd
ja short @@badrec
mov al,sector
cmp al,LAST_SEC1
ja short @@badrec
; phys recordnum = track * spt + sector
PUSHR <cx,dx>
mov ah,RMD
mov al,0 ; spt
call get_dpb_entrie
mov cx,bx ; cx:=spt B:
mov ax,word ptr track
mul cx
add ax,word ptr sector ; ax:=phys record num
mov cx,512 ; 64K = 512 records @ 128 Bytes
mov dx,0
div cx ; ax:= # of 64K, dx:=recordnum
mov cx,1000h
push dx
mul cx
pop dx
mov bx,rdseg_adr
add bx,ax
mov ax,dx
LOG2(RECORD_LEN)
shl ax,cl
xchg bx,ax
POPR <dx,cx>
clc
@@exit:
ret
@@badrec:
stc
jmp short @@exit
calcadr endp
;
read proc
;
; lies einen record von 128 bytes
;
if HARD_DISC_TOO
cmp diskno,HARD_DISC
jnz short @@no_hd
call read_hdisc
mov al,0
ret
@@no_hd:
endif
cmp diskno,RMD
jnz short @@rfdisc
call calcadr ; ax:bx := record addresse
jc short @@badsec
call rdrec
@@exit:xor al,al ; o.k.
ret
@@badsec:
mov al,1
ret
@@rfdisc:
call is_valid_rec ; ax=rec/sec
jc short @@badsec
call read_track
jc short @@badsec
;
; record num ist in ah
; ax:bx := adresse Track Buffer
;
mov al,READ_OP
call get_adr_in_buf ; nach bx
mov ax,ds ; ax := seg track buffer
call rdrec
jmp short @@exit
read endp
if HARD_DISC_TOO
read_hdisc proc
mov ah,READ_FROM_HANDLE
jmp short rw_hdisc
read_hdisc endp
write_hdisc proc
push cx
mov ah,WRITE_TO_HANDLE
call rw_hdisc
pop cx
cmp cl,1
jnz short @@exit
; with cl=1 bdos has signaled that this is a write to directory (=file
; close), so flush msdos file
mov bx,hd_handle
DOS DUPLICATE_HANDLE ; to flush the file
CALLC disp_disk_err
mov bx,ax
DOS CLOSE_FILE
@@exit:
ret
write_hdisc endp
rw_hdisc proc
PUSHA
call @@rw_sec
POPA
ret
@@rw_sec:
; phys recordnum = track * spt + sector
push ax
mov ah,HARD_DISC
mov al,0 ; spt
call get_dpb_entrie
mov cx,bx ; cx:=spt B:
mov ax,word ptr track
mul cx
add ax,word ptr sector ; ax:=phys record num
mov cx,0
mov dx,ax ; cx:dx *= 128
rept 7 ; 128 = 2^7
shl dx,1
rcl cx,1
endm
mov al,0 ; seek from start
mov bx,hd_handle
DOS MOVE_FILE_POINTER ; cx:dx := file length
pop ax
mov dx,dmaad
mov bx,hd_handle
push ds
push es
pop ds
mov cx,128 ; read/write one record
int 21h
pop ds
@@exit:
ret
rw_hdisc endp
endif
; berechne Sektoradresse im Buffer
; a=sector*128+buf
; input: al=READ_OP / WRITE_OP
get_adr_in_buf proc
PUSHR <AX,CX>
call is_dir_track
mov bx,offset dirtrbuf
jz short @@calc
mov bx,offset secbuf
cmp al,READ_OP
jz short @@calc
mov bx,offset wrtbuf
@@calc:
xor ax,ax
mov al,sector
LOG2(RECORD_LEN) ; nach cl
shl ax,cl ; ax *= 128
add bx,ax
POPR <CX,AX>
RET
get_adr_in_buf endp
is_dir_track proc
push ax
mov al,track
cmp al,dirtrack
pop ax
ret
is_dir_track endp
;
;
;
is_valid_rec proc
; Out: cy sector illegal
;
; test auf gueltigen track/sector auf DISC
;
COMMENT *
mov al,track
cmp al,LAST_TRCK0
stc
ja short @@exit
mov al,sector
cmp al,LAST_SEC0
stc
ja short @@exit
; ok, clear cy
*
clc
@@exit:
ret
is_valid_rec endp
;
; lies ganzen Track, falls noch nicht im Speicher
;
read_track proc
;
; Error: cy=1
;
PUSHR <ax,bx,cx,dx>
; der DirTrack wird nur gelesen nach HOME
mov al,track
cmp al,dirtrack
jnz short @@nodir
TSTNCLR home_flag
mov bx,offset dirtrbuf
jnz short @@read_dir_track
clc
jmp short @@exit
@@nodir:
cmp al,last_track_read
jz short @@exit
mov bx,offset secbuf
mov last_track_read,al
@@read_dir_track:
mov r_track,al
call read_phys_track
; ab jetzt CY nicht mehr veraendern !
@@exit:
POPR <dx,cx,bx,ax>
ret
read_track endp
;
; lies ganzen Track, in den geschrieben werden soll, ein,
; falls noch nicht im Speicher
;
read_w_track proc
;
; Error: cy=1
;
PUSHR <ax,bx,cx,dx>
; der DirTrack ist sowieso im Speicher und braucht
; nicht gelesen werden
mov al,track
cmp al,dirtrack
jz short @@exit
cmp al,last_track_written
jz short @@exit
; falls noetig alten Track im WriteBuffer erst
; ausschreiben
call write_out_old_track
mov al,track
mov bx,offset wrtbuf
mov last_track_written,al
mov r_track,al
call read_phys_track
; ab jetzt CY nicht mehr veraendern !
@@exit:
POPR <dx,cx,bx,ax>
ret
read_w_track endp
; physikalische Sektorlaenge herstellen
dos_phys_len proc
PUSHR <es,ax,di>
mov ax,0
mov es,ax
les di,[es:78h]
mov al,ms_phys_sec_len
mov es:[di+3],al
POPR <di,ax,es>
ret
dos_phys_len endp
; INT 1E - Disk Initialization Parameter Table Vector
; far pointer to the diskette base table
; unfortunately this points to ROM within a os/2 vdm
; so it cannot be changed
; (maybe it could be copied to ram)
; physikalische Sektorlaenge herstellen
cpm_phys_len proc
PUSHR <es,ax,di>
mov ax,0
mov es,ax
les di,[es:1Eh*4]
mov al,cpm_phys_sec_len
mov es:[di+3],al
POPR <di,ax,es>
ret
cpm_phys_len endp
rw_phys_track proc ; common for read and write
; in: bp low: fn # for int 13
; bp high: track to read/write
; bx: buf offset
PUSHR <ax,bx,cx,dx>
mov cl,retry_count
xor ch,ch
@@tryrw:
call cpm_phys_len
push es
mov ax,emudata_seg
mov es,ax ; ist sonst z80cpu_seg
push cx
mov dl,cpm_drive ; diskno
mov dh,side
mov cx,bp ; Track
call track_trans
mov cl,first_phys_sec ; physical sector number
mov ax,bp ; op code to al
mov ah,al
mov al,phys_sec_pt ; lies ganzen Track auf einmal
int 13h ; es:bx := track
pop cx
pop es
call dos_phys_len
jnc short @@exit
cmp ah,9
jz dma_bound
; Fehler, Disk Reset und nochmal versuchen
mov ah,0
mov dl,cpm_drive
int 13h ; disk reset
loop @@tryrw
stc ; Error, cy
@@exit:
POPR <dx,cx,bx,ax>
ret
rw_phys_track endp
read_phys_track proc
; der naechste Test sollte eigentlich nicht noetig sein, aber
; mein 386 Ami Bios weigert sich sonst, den gleichen Track nochmal
; zu lesen; jedenfalls kann auch gleich ein Diskwechsel festgestellt
; werden
call tst_disk_change
MOVNZ last_track_read,0ffh
PUSHR <bp>
mov ah,r_track
mov al,2 ; read sector
mov bp,ax
call rw_phys_track
POPR <bp>
ret
read_phys_track endp
write_phys_track proc
PUSHR <bx,bp>
mov bx,outbuf_ptr
mov ah,w_track
mov al,3 ; write sector
mov bp,ax
call rw_phys_track
POPR <bp,bx>
ret
write_phys_track endp
; Trackuebersetzung nur fuer CP/M 86 DS DD noetig
; muss vor phys. read/write Ops aufgerufen werden
;
; in: ch: zu uebersetzender Track
; dh: Disk Side
track_trans proc
cmp ch,phys_tracks
jb short @@exit
cmp cpm86dd_flag,TRUE
jnz short @@nocpm86dd
push ax
mov al,ch
mov ch,79
sub ch,al
pop ax
inc dh ; Seite 1
jmp short @@exit
@@nocpm86dd:
sub ch,40
inc dh ; Seite 1
@@exit:
ret
track_trans endp
tst_disk_change proc
PUSHR <ax,dx>
cmp hd_drive_flag,FALSE
jz short @@exit ; kein Diskwechsel => Z
mov ah,16h
mov dl,cpm_drive
int 13h ; Test auf Wechsel
cmp ah,0 ; kein Diskwechsel => Z
@@exit:
POPR <dx,ax>
ret
tst_disk_change endp
; falls noch nicht geschriebener Track im Speicher ist,
; der kein DirTrack ist, ausschreiben
write_out_old_track proc
cmp write_flag,TRUE
jnz short @@exit
mov al,last_track_written
mov w_track,al
mov outbuf_ptr,offset wrtbuf
call write_phys_track
mov write_flag,FALSE
@@exit:
ret
write_out_old_track endp
;
; den Dir Track auf Disk schreiben
;
write_out_dir proc
call write_out_old_track ; falls noetig
mov al,dirtrack
mov w_track,al
mov outbuf_ptr,offset dirtrbuf
call write_phys_track
ret
write_out_dir endp
;
; versuchen, richtiges physikalisches Format zu finden
; und disc parameter block herrichten
;
; falls autologin_flag = FALSE , kann das Zeitaufwendige login
; entfallen
login_disc proc
push bx
call tst_hd_drive
invalidate_rdbuf ; Track Buffer auf leer setzen
; damit bei read_track gelesen wird
mov write_flag,FALSE ; sonst wird beim naechsten write
; erst alter Schrott ausgeschrieben
cmp autologin_flag,TRUE
jnz @@exit
cmp cpmdrv_set_flg,TRUE
jz short @@no_drset
mov cpm_drive,PHYS_DRV
@@no_drset:
mov al,ms_phys_sec_len
mov cpm_phys_sec_len,al
mov cpm86dd_flag,FALSE
mov retry_count,2 ; Leseversuche herabsetzen,
; damit bei falschem
; Format nicht zuviel Zeit verschwendet wird
@@loop:
cmp breakflag,TRUE
CALLZ prg_exit
; erst mal mit dem zuletzt verwendeten Format versuchen
; aber nicht, wenn letztes CP/M 86 war, da sonst das
; aehnliche Format mit 9 Sektoren nicht erkannt wird
cmp byte ptr phys_sec_pt,8
jz short @@tst_cpm86
mov al,pspt_last
mov phys_sec_pt,al
mov al,phys_tracks_last
mov phys_tracks,al
mov al,frstps_last
mov first_phys_sec,al ; physical sector
mov al,dirtr_last
mov dirtrack,al
inc al
mov r_track,al
mov bx,offset secbuf
call read_phys_track
mov bx,dpb_last
jnc @@goodfmt
;
; CP/M 86 format ?
;
; Bedingungen: Seite 0 muss Sektoren 1-8 haben, aber nicht 1-9
; Seite 1 muss Sektoren 1-8 haben
; setze 9 Phys. Sektoren, bei cp/m 86 mit 8 Sektoren pro Track muss
; es einen Lesefehler geben
@@tst_cpm86:
mov phys_sec_pt,9
mov phys_tracks,40
mov first_phys_sec,1h
mov r_track,1+1
mov dirtrack,1
mov bx,offset secbuf
call read_phys_track
jnc short @@nocpm86
mov phys_sec_pt,8
mov phys_tracks,40
mov first_phys_sec,1h
mov r_track,1+1
mov dirtrack,1
mov bx,offset secbuf
call read_phys_track
jc short @@nocpm86
; Teste noch Seite 2 mit SektorNummer > 39
mov phys_sec_pt,8
mov phys_tracks,40
mov first_phys_sec,1h
mov r_track,40
mov dirtrack,1
mov bx,offset secbuf
call read_phys_track
mov bx,offset dpb_cpm86DS
mov cpm86dd_flag,TRUE
jnc @@goodfmt
mov cpm86dd_flag,FALSE
mov bx,offset dpb_cpm86SS
jmp @@goodfmt
@@nocpm86:
;
; cpc system format ?
;
mov phys_sec_pt,9
mov phys_tracks,40
mov first_phys_sec,41h
mov r_track,2+1
mov dirtrack,2
mov bx,offset secbuf
call read_phys_track
mov bx,offset dpb_cpc
jnc short @@goodfmt
;
; data format ?
;
mov phys_sec_pt,9
mov phys_tracks,40
mov first_phys_sec,0c1h
mov r_track,0+1
mov dirtrack,0
mov bx,offset secbuf
call read_phys_track
mov bx,offset dpb_dta
jnc short @@goodfmt
@@tsteigfrm:
;
; is it own format
;
mov phys_sec_pt,9
mov phys_tracks,40
mov first_phys_sec,1
mov r_track,0+1
mov dirtrack,0
mov bx,offset secbuf
call read_phys_track
mov bx,offset dpb_eig
jnc short @@goodfmt
@@badfmt:
mov bx,offset bad_format
call puts
call crtin
cmp autologin_flag,TRUE ; waehrend conin kann Edit DPH gewes. sein
jnz short @@exit
jmp @@loop
@@goodfmt:
; physikalisches Format merken
mov al,phys_sec_pt
mov pspt_last,al
mov al,first_phys_sec
mov frstps_last,al
mov al,dirtrack
mov dirtr_last,al
mov dpb_last,bx
mov si,bx
mov di,offset dpb0+ZBIOSDISPL
mov cx,size dpb
cld
rep movsb ; es:di := ds:si
@@exit:
pop bx
mov retry_count,5
ret
login_disc endp
write proc
;
; write a record of 128 bytes
;
if HARD_DISC_TOO
cmp diskno,HARD_DISC
jnz short @@no_hd
call write_hdisc
mov al,0
ret
@@no_hd:
endif
cmp diskno,RMD
jnz short wtodisc
call calcadr ; ax:bx := record adress
jc short @@bad
call wrtrec ; buffer := record
jc short @@bad
xor al,al ; write ok
ret
@@bad: mov al,1
ret
write endp
wtodisc proc
push cx
call is_valid_rec ; ax=rec/sec
pop cx
jc short @@badsec
; if track noch nicht im Buffer
; if noch alter Track im Buffer
; alten Track ausschreiben
; endif
; lies neuen Track ein
; endif
; if track ist dirtrack
; if noch alter Track im Buffer
; alten Track ausschreiben
; endif
; dirtrack ausschreiben
; else
; Setze Flag, daß Sektor geschreiben wurde
; endif
;
push cx ; bdos info, ob dirsectoren
call read_w_track
pop cx
jc short @@badsec
;
; record # ist in ah
;
push cx
mov al,WRITE_OP
call get_adr_in_buf
mov ax,ds ; ax:= seg track buffer
call wrtrec
pop cx
cmp cl,1
jz short @@isdir1
call is_dir_track
jz short @@isdir1
mov write_flag,TRUE
mov al,track
mov last_track_written,al
mov al,0 ; o.k.
jmp short @@exit
@@isdir1:
call write_out_dir
jc short @@badsec
mov al,0
jmp short @@exit
@@badsec:
mov al,1
@@exit:
ret
wtodisc endp
;
; In: ax:bx = source adr
;
rdrec proc
;
PUSHR <si,di,bx,cx,ax>
push ds
mov si,bx
mov di,dmaad ; ds MUSS = emudata_seg sein
mov ds,ax
mov cx,RECORD_LEN/2 ; record laenge in words
cld
rep movsw ; es:di := ds:si
pop ds
POPR <ax,cx,bx,di,si>
ret
rdrec endp
;
; In: ax:bx=target adr
;
wrtrec proc
PUSHR <si,di,cx,ax>
push es
push ds
mov si,dmaad ; ACHTUNG: der MOV muss
; vor DS Segmentwechsel sein
mov di,bx
mov bx,ax ; ramdisc
mov ax,es ; cp/m
mov es,bx
mov ds,ax
mov cx,RECORD_LEN/2 ; record laenge in words
cld
rep movsw ; es:di := ds:si
pop ds
pop es
POPR <ax,cx,di,si>
ret
wrtrec endp
emulator_seg ends
end init